---
interface Result {
  category: string
  emoji: string
  slug: string
  terms: string[]
  title: string
}

interface Props {
  collections: Result[]
  posts: Result[]
}

const { collections, posts } = Astro.props
---

<search-results data-collections={JSON.stringify(collections)} data-posts={JSON.stringify(posts)}>
  <div
    data-container="false"
    class="absolute top-full mt-2 w-full rounded-lg border border-stone-300 bg-white py-1 shadow-lg data-[container=false]:hidden data-[container=true]:block"
  >
    <p data-error="false" class="px-4 py-2 text-stone-700 data-[error=false]:hidden">
      No results found.
    </p>

    <ul data-list role="listbox" class="max-h-72 divide-y divide-stone-200 overflow-y-auto"></ul>
  </div>
</search-results>

<script>
  interface Result {
    category: string
    emoji: string
    slug: string
    terms: string[]
    title: string
  }

  class SearchResults extends HTMLElement {
    private collectionItems: Result[] = []
    private postItems: Result[] = []

    private searchResults: Result[] = []

    private enterHandler: (() => void) | null = null
    private escapeHandler: (() => void) | null = null
    private searchHandler: ((searchEvent: Event) => void) | null = null

    connectedCallback() {
      const containerEl = this.querySelector('[data-container]') as HTMLDivElement | null
      const errorEl = this.querySelector('[data-error]') as HTMLParagraphElement | null
      const resultsEl = this.querySelector('[data-list]') as HTMLUListElement | null

      if (!containerEl || !errorEl || !resultsEl) {
        return
      }

      try {
        this.collectionItems = JSON.parse(this.dataset.collections || '[]')
        this.postItems = JSON.parse(this.dataset.posts || '[]')
      } catch {
        return
      }

      this.enterHandler = () => {
        if (this.searchResults.length !== 1) {
          return
        }

        const [searchResult] = this.searchResults

        const returnUrl = new URL(searchResult.slug, globalThis.location.href)

        globalThis.location.href = returnUrl.toString()
      }

      this.escapeHandler = () => {
        this.searchResults = []

        resultsEl.innerHTML = ''

        containerEl.setAttribute('data-container', 'false')
        errorEl.setAttribute('data-error', 'false')
      }

      this.searchHandler = (searchEvent: Event) => {
        const searchQuery = (searchEvent as CustomEvent).detail.value.toLowerCase()

        if (!searchQuery) {
          this.searchResults = []

          resultsEl.innerHTML = ''

          containerEl.setAttribute('data-container', 'false')
          errorEl.setAttribute('data-error', 'false')

          return
        }

        containerEl.setAttribute('data-container', 'true')

        const searchResults = [...this.collectionItems, ...this.postItems]
          .filter((potentialResult) => {
            const resultTerms = potentialResult.terms.map((searchTerm) => searchTerm.toLowerCase())
            const resultTitle = potentialResult.title.toLowerCase()

            return (
              resultTerms.some((resultTerm) => resultTerm.includes(searchQuery)) ||
              resultTitle.includes(searchQuery)
            )
          })
          .sort((resultA, resultB) => {
            const aTitle = resultA.title.toLowerCase()
            const bTitle = resultB.title.toLowerCase()

            const aTitleMatch = aTitle.includes(searchQuery)
            const bTitleMatch = bTitle.includes(searchQuery)

            if (aTitleMatch && !bTitleMatch) {
              return -1
            }

            if (!aTitleMatch && bTitleMatch) {
              return 1
            }

            return 0
          })

        if (searchResults.length === 0) {
          this.searchResults = []

          resultsEl.innerHTML = ''

          errorEl.setAttribute('data-error', 'true')

          return
        }

        this.searchResults = searchResults

        resultsEl.innerHTML = searchResults
          .map(({ category, emoji, slug, title }) => {
            return `
              <li role="option">
                <a
                  href="${slug}"
                  class="block px-4 py-2 font-medium text-pretty text-stone-700 transition-colors hover:bg-stone-100 hover:text-stone-900 focus:ring-2 focus:ring-pink-500 focus:outline-0 focus:ring-inset"
                >
                  <div class="flex gap-2">
                    <span aria-hidden="true">${emoji}</span>
                    <span>${title}</span>
                  </div>

                  ${category ? `<p class="text-sm text-stone-700 mt-1 capitalize">${category}</p>` : ''}
                </a>
              </li>
            `
          })
          .join('')

        errorEl.setAttribute('data-error', 'false')
      }

      document.addEventListener('input:enter', this.enterHandler)
      document.addEventListener('input:escape', this.escapeHandler)
      document.addEventListener('input:search', this.searchHandler)
    }

    disconnectedCallback() {
      if (this.enterHandler && this.escapeHandler && this.searchHandler) {
        document.removeEventListener('input:enter', this.enterHandler)
        document.removeEventListener('input:escape', this.escapeHandler)
        document.removeEventListener('input:search', this.searchHandler)

        this.enterHandler = null
        this.escapeHandler = null
        this.searchHandler = null
      }

      this.collectionItems = []
      this.postItems = []
    }
  }

  customElements.define('search-results', SearchResults)
</script>
